package com.xpalive.config.filter;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.util.StreamUtils;

//https://blog.csdn.net/b644ROfP20z37485O35M/article/details/94001903 在filter中处理request
@Slf4j
public class ChangeBodyHttpServletRequest extends HttpServletRequestWrapper {

    private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
    private static final String MULTIPART_CONTENT_TYPE = "multipart/form-data;";
    private static final String JSON_CONTENT_TYPE = "application/json";

    private final ByteArrayOutputStream cachedContent;

    private ServletInputStream inputStream;
    private BufferedReader reader;

    private final Map<String, Object> extendMap;


    /**
     * Create a new ContentCachingRequestWrapper for the given servlet request.
     *
     * @param request the original servlet request
     */
    public ChangeBodyHttpServletRequest(HttpServletRequest request, Map<String, Object> extendMap) {
        super(request);
        if (!isMultipart()) {
            int contentLength = request.getContentLength();
            this.cachedContent = new ByteArrayOutputStream(
                contentLength >= 0 ? contentLength : 1024);
        } else {
            this.cachedContent = new ByteArrayOutputStream(0);
        }
        this.extendMap = Optional.ofNullable(extendMap).orElseGet(HashMap::new);
    }

    /**
     * Create a new ContentCachingRequestWrapper for the given servlet request.
     *
     * @param request the original servlet request
     */
    public ChangeBodyHttpServletRequest(HttpServletRequest request) {
        this(request, null);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (!isMultipart()) {
            if (this.inputStream == null) {
                byte[] requestBody;
                requestBody = StreamUtils.copyToByteArray(getRequest().getInputStream());
                //change requestBody context
//                Map map = XJsonUtils.mapperIgnoreTimeFormatter.readValue(requestBody, Map.class);
//                map.putAll(extendMap);
//                requestBody = XJsonUtils.mapperIgnoreTimeFormatter.writeValueAsBytes(map);
                final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
                this.inputStream = new ChangeBodyInputStream(bais);
            }
            return this.inputStream;
        } else {
            return super.getInputStream();
        }
    }

    @Override
    public BufferedReader getReader() throws IOException {
        if (!isMultipart()) {
            if (this.reader == null) {
                this.reader = new BufferedReader(
                    new InputStreamReader(getInputStream(), getCharacterEncoding()));
            }
            return this.reader;
        } else {
            return super.getReader();
        }
    }


    @Override
    public Enumeration<String> getParameterNames() {
        Set<String> paramNames = new LinkedHashSet<>();
//        paramNames.add("loginUserId");
        // add some params
        Enumeration<String> names = super.getParameterNames();
        while (names.hasMoreElements()) {
            paramNames.add(names.nextElement());
        }
        return Collections.enumeration(paramNames);

    }

    @Override
    public String[] getParameterValues(String name) {
//        if (name.equals("loginUserId")) {
//            return new String[]{"1"};
//        }
        return super.getParameterValues(name);
    }

    private boolean isFormPost() {
        String contentType = getContentType();
        return (contentType != null && contentType.contains(FORM_CONTENT_TYPE) &&
            HttpMethod.POST.matches(getMethod()));
    }


    /**
     * Return the cached request content as a byte array.
     * <p>The returned array will never be larger than the content cache limit.
     */
    public byte[] getContentAsByteArray() {
        return this.cachedContent.toByteArray();
    }

    /**
     * Template method for handling a content overflow: specifically, a request body being read that
     * exceeds the specified content cache limit.
     * <p>The default implementation is empty. Subclasses may override this to
     * throw a payload-too-large exception or the like.
     *
     * @param contentCacheLimit the maximum number of bytes to cache per request which has just been
     *                          exceeded
     * @since 4.3.6
     */
    protected void handleContentOverflow(int contentCacheLimit) {
    }

    private boolean isMultipart() {
        String contentType = getContentType();
        return contentType != null && contentType.startsWith(MULTIPART_CONTENT_TYPE);
    }
}